#!/bin/sh
#
# %CopyrightBegin%
#
# Copyright Ericsson AB 2003-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# %CopyrightEnd%
#
#
# This is a script to start Erlang/OTP for debugging. PATH is set to
# include this script so if slave nodes are started they will use this
# script as well.
#
#  usage:  cerl [ OPTIONS ] [ ARGS ]
#
#  The OPTIONS are
#
#   -rootdir $MYROOTDIR
#               Run an installed emulator built from this source
#   -debug      Run debug compiled emulator
#   -gdb        Run the debug compiled emulator in emacs and gdb.
#               You have to start beam in gdb using "run".
#   -rgdb       Run the debug compiled emulator in gdb.
#               You have to start beam in gdb using "run".
#   -dump       Dump the bt of all threads in a core.
#   -break F    Run the debug compiled emulator in emacs and gdb and set break.
#               The session is started, i.e. "run" is already don for you.
#   -xxgdb      FIXME currently disabled
#   -purify     Run emulator compiled for purify
#   -quantify   Run emulator compiled for quantify
#   -purecov    Run emulator compiled for purecov
#   -gcov       Run emulator compiled for gcov
#   -valgrind   Run emulator compiled for valgrind
#   -lcnt	Run emulator compiled for lock counting
#   -icount	Run emulator compiled for instruction counting
#   -nox        Unset the DISPLAY variable to disable us of X Windows
#
# FIXME For GDB you can also set the break point using "-break FUNCTION".
# FIXME For GDB you can also point out your own .gdbini......

# These are marked for export
export ROOTDIR
export PROGNAME
export EMU
export BINDIR
export PATH

cargs=
xargs=
cxargs_add() {
    while [ $# -gt 0 ]; do
	cargs="$cargs $1"
	xargs="$xargs $1"
	shift
    done
}

eeargs=
eeargs_add() {
    while [ $# -gt 0 ]; do
	cargs="$cargs $1"
	eeargs="$eeargs $1"
	shift
    done
}

core=

GDB=
GDBBP=
GDBARGS=
TYPE=
debug=
run_valgrind=no

# Default rootdir
ROOTDIR=%SRC_ROOTDIR%
BINDIR="$ROOTDIR/bin/`$ROOTDIR/erts/autoconf/config.guess`"
TARGET=%TARGET%
#BINDIR="$ROOTDIR/bin/%TARGET%"
PROGNAME=$ROOTDIR/bin/cerl
EMU=beam

PRELOADED=$ROOTDIR/erts/preloaded/ebin


while [ $# -gt 0 ]; do
    case "$1" in
	+*)
      # A system parameter!
	    cxargs_add $1
	    shift
      # If next argument does not begin with a hyphen or a plus,
      # it is used as the value of the system parameter.
	    if [ $# -gt 0 ]; then
		case $1 in
		    -*|+*)
			;;
		    *)
			cxargs_add $1
			shift;;
		esac
	    fi;;
	"-instr")
	    cxargs_add $1
	    shift
	    ;;
	"-target")
	    shift
	    BINDIR="$ROOTDIR/bin/$1"
	    shift
	    ;;
	"-rootdir")
	    shift
	    cargs="$cargs -rootdir $1"
	    ROOTDIR="$1"
	    BINDIR=$ROOTDIR/erts-%VSN%/bin
	    shift
	    ;;
	"-display")
	    shift
	    DISPLAY="$1"
	    export DISPLAY
	    shift
	    ;;
	"-nox")
	    shift
	    unset DISPLAY
	    ;;
	"-smp")
	    shift
	    if [ $# -le 0 ]; then
		eeargs_add -smp
	    else
		case $1 in
		    disable)
			shift
			eeargs_add -smpdisable
			;;
		    enable)
			shift
			eeargs_add -smp
			;;
		    *)
			eeargs_add -smp
		esac
	    fi
	    ;;
	"-smpdisable")
	    shift
	    eeargs_add -smpdisable
	    ;;
	"-lcnt")
	    shift
	    cargs="$cargs -lcnt"
	    TYPE=.lcnt
	    ;;
	"-gprof")
	    shift
	    cargs="$cargs -gprof"
	    TYPE=.gprof
	    ;;
	"-debug")
	    shift
	    cargs="$cargs -debug"
	    TYPE=.debug
	    ;;
	"-frmptr")
	    shift
	    cargs="$cargs -frmptr"
	    TYPE=.frmptr
	    ;;
	"-icount")
	    shift
	    cargs="$cargs -icount"
	    TYPE=.icount
	    ;;
	"-dump")
	    shift
	    GDB=dump
	    core="$1"
	    shift
	    ;;
	"-gdb")
	    shift
	    GDB=egdb
	    ;;
	"-rgdb")
	    shift
	    GDB=gdb
	    ;;
	"-break")
	    shift
	    GDB=gdb
	    GDBBP="$GDBBP (insert-string \"break $1\") (comint-send-input)"
	    shift
	    ;;
	"-core")
	    shift
	    GDB=egdb
	    core="$1"
	    shift
	    ;;
	"-rcore")
	    shift
	    GDB=gdb
	    core="$1"
	    shift
	    ;;
#	"-xxgdb")
#	    shift
#	    GDB=xxgdb
#	    ;;
	"-purify")
	    shift
	    cargs="$cargs -purify"
	    TYPE=.purify
	    ;;
	"-quantify")
	    shift
	    cargs="$cargs -quantify"
	    TYPE=.quantify
	    ;;
	"-purecov")
	    shift
	    cargs="$cargs -purecov"
	    TYPE=.purecov
	    ;;
	"-gcov")
	    shift
	    cargs="$cargs -gcov"
	    TYPE=.gcov
	    ;;
	"-valgrind")
	    shift
	    cargs="$cargs -valgrind"
	    TYPE=.valgrind
	    run_valgrind=yes
	    ;;
	*)
	    break
	    ;;
    esac
done


if [ ! -f $BINDIR/erlexec -a -f $ROOTDIR/bin/$TARGET/erlexec ]; then
    # We are in a strange target (I'm looking at you openbsd) where
    # TARGET != config.guess
    BINDIR=$ROOTDIR/bin/$TARGET
fi

PATH=$BINDIR:$ROOTDIR/bin:$PATH
EXEC=$BINDIR/erlexec

PROGNAME="$PROGNAME$cargs"
EMU="$EMU$TYPE"
EMU_NAME=`$EXEC -emu_name_exit $eeargs`

if [ $run_valgrind != yes ]; then
    xargs="$xargs -pz $PRELOADED --"
fi
if [ "x$GDB" = "x" ]; then
    if [ $run_valgrind = yes ]; then
	valversion=`valgrind --version`
	valmajor=`echo $valversion | sed 's,[a-z]*\-\([0-9]*\).*,\1,'`
        valminor=`echo $valversion | sed 's,[a-z]*\-[0-9]*.\([0-9]*\).*,\1,'`
	emu_xargs=`echo $xargs | sed "s|+|-|g"`
	if [ "x$VALGRIND_LOG_XML" = "x" ]; then
	    valgrind_xml=
	    log_file_prefix="--log-file="
	else
	    export VALGRIND_LOG_XML
	    valgrind_xml="--xml=yes"
	    if [ $valmajor -gt 2 -a $valminor -gt 4 ]; then
		log_file_prefix="--xml-file="
	    else
		log_file_prefix="--log-file="
	    fi
	fi
	if [ "x$VALGRIND_LOG_DIR" = "x" ]; then
	    valgrind_log=
	else
	    if [ $valmajor -gt 2 -a $valminor -gt 4 ]; then
		valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU_NAME.log.$$"
	    else
		valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU_NAME.log"
	    fi
	fi
	if [ "x$VALGRIND_MISC_FLAGS" = "x" ]; then
	    valgrind_misc_flags=
	else
	    valgrind_misc_flags="$VALGRIND_MISC_FLAGS"
	fi
	if which taskset > /dev/null && test -e /proc/cpuinfo; then
	    # We only let valgrind utilize one core with "taskset 1" as it can be very slow
	    # on multiple cores (especially with async threads). Valgrind only run one pthread
	    # at a time anyway so there is no point letting it utilize more than one core.
	    # Use $sched_arg to force all schedulers online to emulate multicore.
	    taskset1="taskset 1"
	    ncpu=`cat /proc/cpuinfo | grep -w processor | wc -l`
	    sched_arg="-S$ncpu:$ncpu"
	else
	    taskset1=
	    sched_arg=
	fi

	beam_args=`$EXEC -emu_args_exit ${1+"$@"}`

	# Time for some argument passing voodoo:
	# $beam_args is a list of command line arguments separated by newlines.
	# Make "$@" represent those arguments verbatim (including spaces and quotes).
	SAVE_IFS="$IFS"
	IFS='
'
	set -- $beam_args
	IFS="$SAVE_IFS"
	exec $taskset1 valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" -pz $PRELOADED
    else
	exec $EXEC $eeargs $xargs ${1+"$@"}
    fi
elif [ "x$GDB" = "xgdb" ]; then
    case "x$core" in
	x)
	    # Get emu args to use from erlexec...
	    beam_args=`$EXEC -emu_args_exit ${1+"$@"}`
	    gdbcmd="--args $EMU_NAME $beam_args"
	    ;;
	x/*)
	    gdbcmd="$EMU_NAME ${core}"
	    GDBBP=
	    ;;
	*)
	    dir=`pwd`
	    gdbcmd="$EMU_NAME ${dir}/${core}"
	    GDBBP=
	    ;;
    esac
    cmdfile="/tmp/.cerlgdb.$$"
    echo "source $ROOTDIR/erts/etc/unix/etp-commands" > $cmdfile
    # Fire up gdb in emacs...
    exec gdb $GDBBP -x $cmdfile $gdbcmd
elif [ "x$GDB" = "xegdb" ]; then
    if [ "x$EMACS" = "x" ]; then
	EMACS=emacs
    fi
    
    case "x$core" in
	x)
	    # Get emu args to use from erlexec...
	    beam_args=`$EXEC -emu_args_exit ${1+"$@"} | tr '\n' ' '`
	    gdbcmd="(insert-string \"set args $beam_args\") \
                    (comint-send-input)"
	    ;;
	x/*)
	    gdbcmd="(insert-string \"core ${core}\") (comint-send-input)"
	    GDBBP=
	    ;;
	*)
	    dir=`pwd`
	    gdbcmd="(insert-string \"core ${dir}/${core}\") \
                    (comint-send-input)"
	    GDBBP=
	    ;;
    esac

    if [ "$EMACS_ANNOTATE_LEVEL" != "" ]; then
	GDBARGS="--annotate=$EMACS_ANNOTATE_LEVEL"
    else
        # Set annotation level for gdb in emacs 22 and higher. Seems to
        # be working with level 1 for emacs 22 and level 3 for emacs 23...
	emacs_major=`$EMACS --version | head -1 | sed 's,^[^0-9]*\([0-9]*\).*,\1,g'`
	if [ '!' -z "$emacs_major" -a $emacs_major -gt 23 ]; then
	    GDBARGS="-i=mi "
	elif [ '!' -z "$emacs_major" -a $emacs_major -gt 22 ]; then
	    GDBARGS="--annotate=3 "
	elif [ '!' -z "$emacs_major" -a $emacs_major -gt 21 ]; then
	    GDBARGS="--annotate=1 "
	fi
    fi
    gdbcmd="$gdbcmd $GDBBP \
            (insert-string \"source $ROOTDIR/erts/etc/unix/etp-commands\") \
            (comint-send-input)"
    # Fire up gdb in emacs...
    exec $EMACS --eval "(progn (gdb \"gdb $GDBARGS$EMU_NAME\") $gdbcmd)"
elif [ "x$GDB" = "xdump" ]; then
    cmdfile="/tmp/.cerlgdb.$$"
    case "x$core" in
	x/*)
	    gdbcmd="$EMU_NAME ${core}"
	    ;;
	*)
	    dir=`pwd`
	    gdbcmd="$EMU_NAME ${dir}/${core}"
	    ;;
    esac
    echo "set width 0
set height 0
set verbose off

source $ROOTDIR/erts/etc/unix/etp-commands
thread apply all bt
" > $cmdfile
    exec gdb --batch --command=$cmdfile $gdbcmd
fi
